package vworld.business.room;

import generated.business.room.RoomServicePOA;
import generated.business.room.RoomServicePackage.NullWorldException;
import generated.callback.ClientCallback;
import generated.domain.avatar.Avatar;
import generated.domain.message.Message;
import generated.domain.room.Position;
import generated.domain.room.Room;
import generated.domain.user.User;
import generated.domain.world.World;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.omg.CORBA.ORB;

import vworld.ServerSingleton;

public class RoomServiceServant extends RoomServicePOA {

	private ORB orb;

	public void setOrb(ORB pOrb) {
		orb = pOrb;
	}

	public Room createRoom(String name, Position location, boolean northGate,
			boolean southGate, boolean eastGate, boolean westGate,
			Avatar[] insideAvatars) {
		Room newRoom = new Room();
		newRoom.setName(name);
		newRoom.setLocation(location);
		newRoom.setNorthGate(northGate);
		newRoom.setSouthGate(southGate);
		newRoom.setEastGate(eastGate);
		newRoom.setWestGate(westGate);
		newRoom.setInsideAvatars(insideAvatars);
		ServerSingleton.getInstance().ROOM_DAO.createRoom(newRoom);
		return newRoom;
	}

	public Avatar[] getInsideAvatars(Room room) {
		return ServerSingleton.getInstance().ROOM_DAO
				.getInsideAvatars(room.name);
	}

	public Position getLocation(Room room) {
		// TODO Auto-generated method stub
		return null;
	}

	public Room getRoomByName(String roomName) {
		return ServerSingleton.getInstance().ROOM_DAO.getRoom(roomName);
	}

	public Room joinRoom(ClientCallback client, Avatar avatar,
			String joinedRoomName) {
		avatar.setState(true);
		User u = ServerSingleton.getInstance().avatarImpl.getUser(avatar);
		u.getAvatar().setState(true);
		ServerSingleton.getInstance().USER_DAO.saveOrUpdateUser(u);

		// retreive joined room from db
		Room joinedRoom = ServerSingleton.getInstance().ROOM_DAO
				.getRoom(joinedRoomName);
		Avatar[] roomAvatars = joinedRoom.getInsideAvatars();
		Avatar[] newRoomAvatars = new Avatar[roomAvatars.length + 1];
		boolean avatarAdded = false;

		// check if the avatar is already present in the room
		boolean alreadyPresent = false;
		for (int i = 0; i < roomAvatars.length; i++) {
			if (roomAvatars[i] == avatar) {
				alreadyPresent = true;
				break;
			}
		}

		// if not already present...
		if (!alreadyPresent) {
			// ... added to the joined room
			for (int i = 0; i < roomAvatars.length; i++) {
				newRoomAvatars[i] = roomAvatars[i];
			}
			newRoomAvatars[roomAvatars.length] = avatar;
			joinedRoom.setInsideAvatars(newRoomAvatars);
			ServerSingleton.getInstance().ROOM_DAO.saveOrUpdateRoom(joinedRoom);
			avatarAdded = true;
		}

		if (avatarAdded) {
			String message = avatar.getName() + " has joined "
					+ joinedRoom.getName();

			// update joined client ref
			client.roomName(joinedRoomName);
			updateClientCallbackRef(client);

			// notification
			for (Map.Entry<String, ClientCallback> e : ServerSingleton
					.getInstance().REGISTERED_CLIENT_MAP.entrySet()) {
				if (e.getValue().roomName().equals(joinedRoom.getName())) {
					// notify all avatars in the joined room
					e.getValue()
							.doNotifyOnJoinOrExit(avatar.getName(), message);
				} else if (e.getValue().userRole().equalsIgnoreCase(
						"administrator")) {
					// prevent admin in case he is watching the left room
					e.getValue().doNotifyAdminOnJoinOrExit(
							joinedRoom.getName(), avatar.getName(), message);
				}
			}
		}

		// update admin's worlds view
		for (ClientCallback cl : ServerSingleton.getInstance()
				.getAdminCallBackList()) {
			cl.updateWorldsView(joinedRoom.name);
		}

		return joinedRoom;
	}

	public Room exitRoom(ClientCallback client, Avatar avatar,
			String leftRoomName) {
		avatar.setState(false);
		User u = ServerSingleton.getInstance().avatarImpl.getUser(avatar);
		u.getAvatar().setState(false);
		ServerSingleton.getInstance().USER_DAO.saveOrUpdateUser(u);

		// retreive left room from db
		Room leftRoom = ServerSingleton.getInstance().ROOM_DAO
				.getRoom(leftRoomName);
		Avatar[] roomAvatars = leftRoom.getInsideAvatars();

		// transformation of avatars array to avatars list
		List<Avatar> newRoomAvatarsList = new ArrayList<Avatar>();
		for (int i = 0; i < roomAvatars.length; i++) {
			newRoomAvatarsList.add(roomAvatars[i]);
		}

		boolean avatarDeleted = false;
		Avatar tempAvatar = null;
		for (Avatar a : newRoomAvatarsList) {
			if (avatar.getId() == a.getId()) {
				tempAvatar = a;
				break;
			}
		}
		avatarDeleted = newRoomAvatarsList.remove(tempAvatar);

		if (avatarDeleted) {
			// update the left room in DB
			leftRoom.setInsideAvatars(newRoomAvatarsList
					.toArray(new Avatar[newRoomAvatarsList.size()]));
			ServerSingleton.getInstance().ROOM_DAO.saveOrUpdateRoom(leftRoom);

			// update left client ref
			client.roomName("");
			updateClientCallbackRef(client);

			// notify all others room's avatars
			String message = avatar.getName() + " has left "
					+ leftRoom.getName();
			for (Map.Entry<String, ClientCallback> e : ServerSingleton
					.getInstance().REGISTERED_CLIENT_MAP.entrySet()) {
				if (e.getValue().roomName().equals(leftRoom.getName())) {
					// prevent others avatars
					e.getValue()
							.doNotifyOnJoinOrExit(avatar.getName(), message);
				} else if (e.getValue().userRole().equalsIgnoreCase(
						"administrator")) {
					// prevent admin in case he is watching the left room
					e.getValue().doNotifyAdminOnJoinOrExit(leftRoom.getName(),
							avatar.getName(), message);
				}
			}
		}

		// update admin's worlds view
		for (ClientCallback cl : ServerSingleton.getInstance()
				.getAdminCallBackList()) {
			cl.updateWorldsView(leftRoom.name);
		}

		return leftRoom;
	}

	public void sendGlobalMessage(String originRoomName, Message message) {
		for (Map.Entry<String, ClientCallback> e : ServerSingleton
				.getInstance().REGISTERED_CLIENT_MAP.entrySet()) {
			if (e.getValue().roomName().equals(originRoomName)) {
				// send message to all avatars of the room
				e.getValue().doNotifyOnGlobalMessage(message.getSender(),
						message.getDate(), message.getContent());
			} else if (e.getValue().userRole()
					.equalsIgnoreCase("administrator")) {
				// send message to admin in case he is watching the room
				e.getValue().doNotifyOnGlobalMessageinWatchedRoom(
						originRoomName, message.getSender(), message.getDate(),
						message.getContent());
			}
		}
	}

	public void sendPersonalMessage(Message message) {
		for (Map.Entry<String, ClientCallback> e : ServerSingleton
				.getInstance().REGISTERED_CLIENT_MAP.entrySet()) {
			if (message.getReceiver().equals(e.getValue().avatarName())) {
				e.getValue().doNotifyOnPersonalMessage(message.getSender(),
						message.getDate(), message.getContent());
				break;
			}
		}
	}

	private void updateClientCallbackRef(ClientCallback client) {
		if (client != null)
			ServerSingleton.getInstance().REGISTERED_CLIENT_MAP.put(client
					.userLogin(), client);
	}

	public User notifyRoomOnLoginOrLogout(ClientCallback client, User u,
			boolean state) {
		User user = u;
		user.getAvatar().setState(state);
		ServerSingleton.getInstance().USER_DAO.saveOrUpdateUser(user);

		String avatarName = user.getAvatar().getName();
		String message = "";

		if (state) {
			message = avatarName + " has connected!";
		} else {
			message = avatarName + " has disconnected!";
		}

		for (Map.Entry<String, ClientCallback> e : ServerSingleton
				.getInstance().REGISTERED_CLIENT_MAP.entrySet()) {
			if (e.getValue().roomName().equals(client.roomName())) {
				// prevent others avatars
				e.getValue().doNotifyOnAvatarLoginOrLogout(avatarName, message);
			} else if (e.getValue().userRole()
					.equalsIgnoreCase("administrator")) {
				// prevent admin in case he is watching the left room
				e.getValue().doNotifyAdminOnAvatarLoginOrLogout(
						client.roomName(), avatarName, message);
			}
		}

		// update admin's worlds view
		for (ClientCallback cl : ServerSingleton.getInstance()
				.getAdminCallBackList()) {
			cl.updateWorldsView(client.roomName());
		}

		return user;
	}

	public World getWorldByRoomName(String roomName) throws NullWorldException {
		World w = ServerSingleton.getInstance().WORLD_DAO
				.getWorldByRoomName(roomName);
		if (w != null) {
			return w;
		} else {
			throw new NullWorldException("No world corresponds to this room");
		}
	}
}
